package com.moli.iov.alarm.remote.impl;

import com.moli.iov.alarm.constants.Constants;
import com.moli.iov.alarm.enums.IsAlarmingEnum;
import com.moli.iov.alarm.model.dto.AlarmVehicleInfoDto;
import com.moli.iov.alarm.model.dto.req.AlarmHandlingDto;
import com.moli.iov.alarm.model.dto.req.AlarmInfoHandlerRecordReqDto;
import com.moli.iov.alarm.model.dto.req.AlarmInfoReqDto;
import com.moli.iov.alarm.model.dto.req.AlarmMeAuditReqDto;
import com.moli.iov.alarm.model.dto.res.AlarmDetailDto;
import com.moli.iov.alarm.model.dto.res.AlarmInfoHandlerRecordDto;
import com.moli.iov.alarm.model.dto.res.AlarmInfoResDto;
import com.moli.iov.alarm.model.dto.res.VehicleFenceExtend;
import com.moli.iov.alarm.model.po.AlarmInfo;
import com.moli.iov.alarm.model.po.AlarmInfoHandlerRecord;
import com.moli.iov.alarm.remote.IAlarmInfoHandlerRecordService;
import com.moli.iov.alarm.remote.IAlarmRemoteService;
import com.moli.iov.alarm.service.IAlarmService;
import com.moli.iov.base.model.dto.SysParamSettingsCacheBean;
import com.moli.iov.base.model.dto.req.VehicleBaseAuthReqDto;
import com.moli.iov.base.model.po.VehicleFence;
import com.moli.iov.base.remote.ISysParamSettingsRemoteService;
import com.moli.iov.base.remote.IVehicleBaseAuthRemoteService;
import com.moli.iov.base.remote.IVehicleFenceRemoteService;
import com.moli.iov.cache.RedisUtil;
import com.moli.iov.enums.UserTypeEnum;
import com.moli.iov.exception.BusinessException;
import com.moli.iov.handle.alarm.constants.Constant;
import com.moli.iov.handle.alarm.enums.AlarmCodeEnum;
import com.moli.iov.handle.alarm.enums.AlarmLevelEnum;
import com.moli.iov.handle.alarm.enums.AlarmStatusEnum;
import com.moli.iov.handle.alarm.enums.MeAuditStatusEnum;
import com.moli.iov.model.dto.req.PageExtend;
import com.moli.iov.rest.RestResponse;
import com.moli.iov.util.CommonUtils;
import com.moli.iov.util.DateUtil;
import com.moli.iov.util.StringUtil;
import com.moli.iov.util.ValidationUtils;

import java.util.*;

import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

/**
 * <p>
 * 报警业务
 * </p>
 *
 * @author zql
 * @since 2019/9/3 13:20
 */
@Slf4j
@Service(group = "${service.group}")
public class AlarmRemoteServiceImpl implements IAlarmRemoteService {

    @Autowired
    private IAlarmService alarmService;
    @Autowired
    private IAlarmInfoHandlerRecordService iAlarmInfoHandlerRecordService;
    @Autowired
    private RedisUtil redisUtil;

    @Reference(group = "${service.group}", check = false)
    private ISysParamSettingsRemoteService sysParamSettingsRemoteService;

    @Reference(group = "${service.group}", check = false)
    private IVehicleFenceRemoteService vehicleFenceRemoteService;
    @Reference(group = "${service.group}", check = false)
    private IVehicleBaseAuthRemoteService iVehicleBaseAuthRemoteService;

    /**
     * 分页查询报警列表
     *
     * @param alarmInfoReqDto
     * @return
     */
    @Override
    public PageExtend<AlarmInfoResDto> listPageAlarmInfo(AlarmInfoReqDto alarmInfoReqDto) {
        if (UserTypeEnum.COMPANY.getFlag() == alarmInfoReqDto.getSysUserType()) {
            alarmInfoReqDto.setCompanyId(alarmInfoReqDto.getSysCompanyId());
        }

        PageExtend<AlarmInfoResDto> alarmInfoDtoPageExtend = alarmService.listPageAlarmInfo(alarmInfoReqDto);
        return assembleAlarmInfoDto(alarmInfoDtoPageExtend);
    }


    /**
     * 报警详情
     *
     * @param alarmInfoReqDto
     * @return
     */
    @Override
    public RestResponse<AlarmInfoResDto> getAlarmInfo(AlarmInfoReqDto alarmInfoReqDto) {

        if (alarmInfoReqDto == null || StringUtil.isBlank(alarmInfoReqDto.getAlarmId())) {
            return RestResponse.failure("报警id不能为空");
        }

        AlarmInfoReqDto alarmInfoReqDtoQuery = new AlarmInfoReqDto();
        alarmInfoReqDtoQuery.setAlarmId(alarmInfoReqDto.getAlarmId());
        AlarmInfoResDto alarmInfo = alarmService.getAlarmInfo(alarmInfoReqDtoQuery);

        if (alarmInfo == null) {
            return RestResponse.failure("报警id不存在");
        }

        if (alarmInfoReqDto.getSysUserType() == UserTypeEnum.COMPANY.getFlag() &&
                !alarmInfoReqDto.getSysCompanyId().equals(alarmInfo.getCompanyId())) {
            return RestResponse.failure("不是本企业车辆，无权操作");
        }

        if (alarmInfo.getMeAuditStatus() != null) {
            alarmInfo.setMeAuditStatusStr(MeAuditStatusEnum.getMsg(alarmInfo.getMeAuditStatus()));
        }

        if ((alarmInfo.getAlarmCode() == AlarmCodeEnum.IN_FENCE.getFlag() ||
                alarmInfo.getAlarmCode() == AlarmCodeEnum.OUT_FENCE.getFlag()) &&
                alarmInfo.getFenceInfo() != null) {
            alarmInfo = assembleFenceInfo(alarmInfo);
        }

        AlarmVehicleInfoDto alarmVehicleInfoDto = alarmInfo.getAlarmVehicleInfo();
        String brandName = StringUtil.isNotBlank(alarmVehicleInfoDto.getBrandName()) ? alarmVehicleInfoDto.getBrandName() : "";
        String seriesName = StringUtil.isNotBlank(alarmVehicleInfoDto.getVehicleSeriesName()) ? alarmVehicleInfoDto.getVehicleSeriesName() : "";
        String typeName = StringUtil.isNotBlank(alarmVehicleInfoDto.getVehicleTypeName()) ? alarmVehicleInfoDto.getVehicleTypeName() : "";
        String vehicleModelStr = brandName + seriesName + typeName;
        alarmInfo.setVehicleModelStr(vehicleModelStr);
        return RestResponse.success(alarmInfo);
    }

    /**
     * 组装围栏信息
     *
     * @param alarmInfoResDto
     * @return
     */
    private AlarmInfoResDto assembleFenceInfo(AlarmInfoResDto alarmInfoResDto) {
        RestResponse restResponse = vehicleFenceRemoteService
                .VehicleFenceById(alarmInfoResDto.getFenceInfo().getFenceId());

        if (restResponse != null &&
                restResponse.flag &&
                restResponse.getData() != null) {
            VehicleFence vehicleFence = (VehicleFence) restResponse.getData();
            VehicleFenceExtend vehicleFenceExtend = new VehicleFenceExtend();
            CommonUtils.copyProperties(vehicleFence, vehicleFenceExtend);
            alarmInfoResDto.setVehicleFenceExtend(vehicleFenceExtend);
        }

        return alarmInfoResDto;
    }


    /**
     * 组装AlarmInfoDto
     *
     * @param alarmInfoDtoPageExtend
     * @return
     */
    private PageExtend<AlarmInfoResDto> assembleAlarmInfoDto(PageExtend<AlarmInfoResDto> alarmInfoDtoPageExtend) {

        List<AlarmInfoResDto> alarmInfoDtos = alarmInfoDtoPageExtend.getRecords();
        for (AlarmInfoResDto alarmInfoDto : alarmInfoDtos) {
            try {
                if (AlarmCodeEnum.OFF_LINE.getFlag() == alarmInfoDto.getAlarmCode()) {
                    long lastDate = alarmInfoDto.getLastReportDate() == null ? alarmInfoDto.getBeginTime().getTime() :
                            alarmInfoDto.getLastReportDate().getTime();
                    long endTime = System.currentTimeMillis();
                    if (alarmInfoDto.getStatus() == AlarmStatusEnum.ALARM_END.getFlag()) {
                        if (alarmInfoDto.getEndTime() != null) {
                            endTime = alarmInfoDto.getEndTime().getTime();
                        }
                    }
                    long duration = endTime - lastDate;
                    String offLineTimeStr = DateUtil.formatTimeStr(duration);
                    String memo = Constants.OFFLINE_MEMO + offLineTimeStr;
                    alarmInfoDto.setMemo(memo);
                }

                if (alarmInfoDto.getMeAuditStatus() != null) {
                    alarmInfoDto.setMeAuditStatusStr(MeAuditStatusEnum.getMsg(alarmInfoDto.getMeAuditStatus()));
                }
                AlarmVehicleInfoDto alarmVehicleInfoDto = alarmInfoDto.getAlarmVehicleInfo();
                String brandName = StringUtil.isNotBlank(alarmVehicleInfoDto.getBrandName()) ? alarmVehicleInfoDto.getBrandName() : "";
                String seriesName = StringUtil.isNotBlank(alarmVehicleInfoDto.getVehicleSeriesName()) ? alarmVehicleInfoDto.getVehicleSeriesName() : "";
                String typeName = StringUtil.isNotBlank(alarmVehicleInfoDto.getVehicleTypeName()) ? alarmVehicleInfoDto.getVehicleTypeName() : "";
                String vehicleModelStr = brandName + seriesName + typeName;
                alarmInfoDto.setVehicleModelStr(vehicleModelStr);

                alarmInfoDto.setStatusName(AlarmStatusEnum.getMsg(alarmInfoDto.getStatus()));
                alarmInfoDto.setAlarmLevelStr(AlarmLevelEnum.getMsg(alarmInfoDto.getAlarmLevel()));

            } catch (Exception e) {
                log.error("分页查询报警列表 组装AlarmInfoDto数据异常", e);
            }
        }

        return alarmInfoDtoPageExtend;
    }

    /**
     * 忽略报警
     *
     * @param alarmInfoReqDto
     * @return
     */
    @Override
    public RestResponse ignore(AlarmInfoReqDto alarmInfoReqDto) {

        if (UserTypeEnum.COMPANY.getFlag() == alarmInfoReqDto.getSysUserType()) {
            alarmInfoReqDto.setCompanyId(alarmInfoReqDto.getSysCompanyId());
        }

        AlarmInfoResDto alarmInfoDto = ignoreValid(alarmInfoReqDto);

        alarmInfoReqDto.setStatus(AlarmStatusEnum.IGNORE.getFlag());
        alarmInfoReqDto.setEndTime(new Date());

        alarmService.ignore(alarmInfoReqDto);

        String key = Constant.IGNORE_ALARM + alarmInfoDto.getVin() + ":" + alarmInfoDto.getAlarmCode();
        long ignoreTime = getIgnoreTime(alarmInfoDto.getVin(), alarmInfoDto.getAlarmCode());
        log.info("忽略报警 vin:{} 时长：{}", alarmInfoDto.getVin(), ignoreTime);
        redisUtil.set(key, alarmInfoDto.getVin(), ignoreTime);

        return RestResponse.success();
    }

    /**
     * 参数校验
     *
     * @param alarmInfoReqDto
     */
    private AlarmInfoResDto ignoreValid(AlarmInfoReqDto alarmInfoReqDto) {
        if (alarmInfoReqDto == null || StringUtil.isBlank(alarmInfoReqDto.getAlarmId())) {
            throw new BusinessException("报警编号不能为空");
        }

        if (StringUtil.isBlank(alarmInfoReqDto.getIgnoreMemo())) {
            throw new BusinessException("原因说明不能为空");
        }
        AlarmInfoReqDto alarmInfoDtoQuery = new AlarmInfoReqDto();
        alarmInfoDtoQuery.setAlarmId(alarmInfoReqDto.getAlarmId());
        if (UserTypeEnum.COMPANY.getFlag() == alarmInfoReqDto.getSysUserType()) {
            alarmInfoDtoQuery.setCompanyId(alarmInfoReqDto.getCompanyId());
        }
        List<AlarmInfoResDto> alarmInfoDtos = alarmService.listAlarmInfo(alarmInfoDtoQuery);
        if (alarmInfoDtos == null || alarmInfoDtos.isEmpty() || alarmInfoDtos.get(0) == null) {
            throw new BusinessException("忽略报警失败，当前报警不存在");
        }

        AlarmInfoResDto alarmInfoDto = alarmInfoDtos.get(0);
        if (AlarmStatusEnum.IGNORE.getFlag() == alarmInfoDto.getStatus()) {
            throw new BusinessException("忽略报警失败，当前报警已忽略");
        }
        if (AlarmStatusEnum.ALARM_END.getFlag() == alarmInfoDto.getStatus()) {
            throw new BusinessException("忽略报警失败，当前报警已解除");
        }

        if (!AlarmCodeEnum.isIgnore(alarmInfoDto.getAlarmCode())) {
            throw new BusinessException("忽略报警失败，当前报警类型不允许忽略");
        }

        return alarmInfoDto;
    }

    /**
     * 获取忽略时长
     * SOC低报警阈值 soc; 蓄电池低电压报警阈值 lowVoltage; 失联标准设置 offlineAlarmDuration;
     * 失联报警忽略规则 ignoreAlarmDuration; 围栏报警忽略规则 fenceAlarmDuration;
     * 里程异变忽略规则 mileageAlarmDuration
     *
     * @param vin
     * @param alarmCode
     * @return
     */
    private long getIgnoreTime(String vin, int alarmCode) {

        RestResponse<Map<String, SysParamSettingsCacheBean>> restResponse =
                sysParamSettingsRemoteService.getSysParamSettingsCacheBeanByVin(Arrays.asList(vin));

        if (restResponse == null || !restResponse.flag || restResponse.getData() == null) {
            return Constant.IGNORE_ALARM_TIME;
        }

        Map<String, SysParamSettingsCacheBean> map = restResponse.getData();
        SysParamSettingsCacheBean sysParamSettingsCacheBean = map.get(vin);
        if (sysParamSettingsCacheBean == null) {
            return Constant.IGNORE_ALARM_TIME;
        }

        if (AlarmCodeEnum.OFF_LINE.getFlag() == alarmCode &&
                sysParamSettingsCacheBean.getIgnoreAlarmDuration() != null) {
            return getDuration(sysParamSettingsCacheBean.getIgnoreAlarmDuration());
        }

        if (AlarmCodeEnum.MILEAGE_EXCEPTION_CHANGE.getFlag() == alarmCode &&
                sysParamSettingsCacheBean.getMileageAlarmDuration() != null) {

            return getDuration(sysParamSettingsCacheBean.getMileageAlarmDuration());
        }

        boolean isT = (AlarmCodeEnum.OUT_FENCE.getFlag() == alarmCode ||
                AlarmCodeEnum.IN_FENCE.getFlag() == alarmCode) &&
                sysParamSettingsCacheBean.getFenceAlarmDuration() != null;
        if (isT) {
            return getDuration(sysParamSettingsCacheBean.getFenceAlarmDuration());
        }

        return Constant.IGNORE_ALARM_TIME;
    }

    private long getDuration(Double ignoreAlarmDuration) {
        Double duration = ignoreAlarmDuration * Constants.HOUR_TO_SECOND;
        return duration.longValue();
    }

    /**
     * 里程异变列表
     *
     * @param vins
     * @return
     */
    @Override
    public RestResponse<List<AlarmInfo>> listMileageExceptionChange(List<String> vins) {

        AlarmInfoReqDto alarmInfoReqDto = new AlarmInfoReqDto();
        alarmInfoReqDto.setVins(vins);
        alarmInfoReqDto.setAlarmCode(AlarmCodeEnum.MILEAGE_EXCEPTION_CHANGE.getFlag());
        alarmInfoReqDto.setNeMeAuditStatus(MeAuditStatusEnum.INVALID.getFlag());
        return RestResponse.success(alarmService.listAlarmInfoOfThird(alarmInfoReqDto));
    }

    /**
     * 里程异变处理 审核
     *
     * @param alarmMeAuditReqDto
     */
    @Override
    public RestResponse meAudit(AlarmMeAuditReqDto alarmMeAuditReqDto) {

        meAuditValid(alarmMeAuditReqDto);

        alarmMeAuditReqDto.setStatus(AlarmStatusEnum.ALARM_END.getFlag());

        alarmService.meAudit(alarmMeAuditReqDto);

        return RestResponse.success();
    }

    /**
     * 里程异变处理 审核 校验
     *
     * @param alarmMeAuditReqDto
     */
    private void meAuditValid(AlarmMeAuditReqDto alarmMeAuditReqDto) {

        ValidationUtils.restResponseValidate(alarmMeAuditReqDto, AlarmMeAuditReqDto.Audit.class);

        AlarmInfoReqDto alarmInfoReqDto = new AlarmInfoReqDto();
        alarmInfoReqDto.setAlarmId(alarmMeAuditReqDto.getAlarmId());
        AlarmInfoResDto alarmInfo = alarmService.getAlarmInfo(alarmInfoReqDto);

        if (alarmInfo == null) {
            throw new BusinessException("报警id不存在");
        }

        if (alarmMeAuditReqDto.getSysUserType() == UserTypeEnum.COMPANY.getFlag() &&
                alarmInfo.getCompanyId() != null &&
                !alarmMeAuditReqDto.getSysCompanyId().equals(alarmInfo.getCompanyId())) {
            throw new BusinessException("不是本企业车辆，无权操作");
        }
    }

    /**
     * 获取vin 正在报警的数量
     *
     * @param alarmInfoReqDto
     * @return
     */
    @Override
    public RestResponse<Map<String, Integer>> getTotalAlarmingByVins(
            AlarmInfoReqDto alarmInfoReqDto) {

        getTotalAlarmingByVinsValid(alarmInfoReqDto);

        Map<String, Integer> totalAlarmingMap = alarmService.getTotalAlarming(alarmInfoReqDto);
        return RestResponse.success(totalAlarmingMap);
    }

    /**
     * 报警状态
     *
     * @param alarmInfoReqDto
     * @return
     */
    @Override
    public RestResponse<AlarmDetailDto> getAlarmDetailByVin(AlarmInfoReqDto alarmInfoReqDto) {

        if (alarmInfoReqDto == null || StringUtil.isBlank(alarmInfoReqDto.getVin())) {
            return RestResponse.failure("vin不能为空");
        }

        alarmInfoReqDto.setStatus(AlarmStatusEnum.ALARMING.getFlag());
        List<AlarmInfoResDto> alarmInfos = alarmService.listAlarmInfo(alarmInfoReqDto);

        AlarmDetailDto alarmDetailDto = AlarmDetailDto.newInstance();
        alarmInfos.forEach(info -> {
            if (AlarmCodeEnum.isFence(info.getAlarmCode())) {
                alarmDetailDto.setIsFence(IsAlarmingEnum.YES.getFlag());
                return;
            }

            if (AlarmCodeEnum.isOffLine(info.getAlarmCode())) {
                alarmDetailDto.setIsOffLine(IsAlarmingEnum.YES.getFlag());
                return;
            }

            if (AlarmCodeEnum.isMileExcep(info.getAlarmCode())) {
                alarmDetailDto.setIsMileExcep(IsAlarmingEnum.YES.getFlag());
                return;
            }

            if (AlarmCodeEnum.isTerminal(info.getAlarmCode())) {
                alarmDetailDto.setIsTerminal(IsAlarmingEnum.YES.getFlag());
                return;
            }
        });
        return RestResponse.success(alarmDetailDto);
    }

    @Override
    public RestResponse alarmHandling(AlarmHandlingDto dto) {
        ValidationUtils.validateBean(dto);
        AlarmInfoResDto alarmInfo = alarmService.getAlarmInfo(dto.getAlarmId());
        if (Objects.isNull(alarmInfo)) {
            throw new BusinessException("报警不存在");
        }
        if (dto.getType().intValue() == 1) {
            AlarmInfoReqDto ignore = new AlarmInfoReqDto();
            ignore.setStatus(AlarmStatusEnum.IGNORE.getFlag());
            ignore.setAlarmId(dto.getAlarmId());
            ignore.setIgnoreUserId(dto.getSysUserId());
            ignore.setIgnoreUserName(dto.getSysAccount());
            ignore.setIgnoreMemo(dto.getNote());
            ignore.setIsHandle(true);
            alarmService.ignore(ignore);
        } else {
            VehicleBaseAuthReqDto vehicleBaseAuthReqDto = new VehicleBaseAuthReqDto();
            vehicleBaseAuthReqDto.setVin(dto.getVin());
            vehicleBaseAuthReqDto.setSysUserId(dto.getSysUserId());
            //里程失联
            if (AlarmCodeEnum.OFF_LINE.getFlag() == alarmInfo.getAlarmCode()) {
                vehicleBaseAuthReqDto.setLostAlarmAuthStatus(0);
                iVehicleBaseAuthRemoteService.updateVehicleBaseAuth(vehicleBaseAuthReqDto);
                //围栏报警
            } else if (AlarmCodeEnum.OUT_FENCE.getFlag() == alarmInfo.getAlarmCode() || AlarmCodeEnum.IN_FENCE.getFlag() == alarmInfo.getAlarmCode()) {
              vehicleBaseAuthReqDto.setFenceAlarmAuthStatus(0);
                iVehicleBaseAuthRemoteService.updateVehicleBaseAuth(vehicleBaseAuthReqDto);
            }
            AlarmInfoReqDto relieve = new AlarmInfoReqDto();
            relieve.setAlarmId(dto.getAlarmId());
            relieve.setStatus(AlarmStatusEnum.ALARM_END.getFlag());
            relieve.setIsHandle(true);
            alarmService.relieve(relieve);
        }
        AlarmInfoHandlerRecord record = new AlarmInfoHandlerRecord();
        record.setAlarmId(dto.getAlarmId());
        record.setCreateDate(new Date());
        record.setNote(dto.getNote());
        record.setOperatorId(dto.getSysUserId());
        record.setOperatorName(dto.getSysRealName());
        iAlarmInfoHandlerRecordService.saveAlarmInfoHandlerRecord(record);
        return RestResponse.success();
    }

    @Override
    public RestResponse getAlarmInfoHandlerRecordList(AlarmInfoHandlerRecordReqDto dto) {
        List<AlarmInfoHandlerRecord> res = iAlarmInfoHandlerRecordService.getAlarmInfoHandlerRecord(dto.getAlarmId());
        return RestResponse.success(toAlarmInfoHandlerRecordDto(res));
    }

    /**
     * toDto
     * @param res
     * @return
     */
    private List<AlarmInfoHandlerRecordDto> toAlarmInfoHandlerRecordDto(List<AlarmInfoHandlerRecord> res) {
        List<AlarmInfoHandlerRecordDto> recordDtos=new ArrayList<>();
        if (!CollectionUtils.isEmpty(res)){
            for (AlarmInfoHandlerRecord re : res) {
                AlarmInfoHandlerRecordDto  dto=new AlarmInfoHandlerRecordDto();
                CommonUtils.copyProperties(re, dto);
                recordDtos.add(dto);
            }
            return recordDtos;
        }
        return null;

    }

    /**
     * 获取vin 正在报警的数量 校验
     *
     * @param alarmInfoReqDto
     * @return
     */
    private void getTotalAlarmingByVinsValid(AlarmInfoReqDto alarmInfoReqDto) {
        if (alarmInfoReqDto == null || alarmInfoReqDto.getVins() == null || alarmInfoReqDto.getVins().isEmpty()) {
            throw new BusinessException("车架号不能为空");
        }
    }
}
